package com.mycat.chat.two.socket.server;

import com.mycat.chat.two.util.Constants;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * 聊天系统基于NIO服务端
 *
 * @author roger.
 * @email 190642964@qq.com
 * @create 2017-02-23
 */
public abstract class ChatNIOServer {

    protected ServerSocketChannel server;
    protected Selector selector;
    protected ByteBuffer receiveBuffer = ByteBuffer.allocate(1024);
    protected ByteBuffer sendBuffer = ByteBuffer.allocate(1024);
    Map<SelectionKey, String> sessionMsg = new HashMap<>();

    public ChatNIOServer() {
        try {
            // 打开基于NIO服务端的通道
            server = ServerSocketChannel.open();
            // 打开端口，开启多路复用
            server.socket().bind(new InetSocketAddress(Constants.NIO_PORT));
            // 设置为非阻塞
            server.configureBlocking(false);
            // 开启Selector
            selector = Selector.open();
            // 服务端正式开启
            server.register(selector, SelectionKey.OP_ACCEPT);

            System.out.println("ChatNIOServer服务端正常启动....监听端口: " + Constants.NIO_PORT);
        } catch (IOException e) {
            System.out.println("ChatNIOServer服务端启动出现异常..... ");
            e.printStackTrace();
        }
    }

    /**
     * 通过轮询获取Selector中注册信息
     *
     * @throws IOException
     */
    public void listener() throws IOException {
        // Selector开起轮询机制
        while (true) {
            // 判断当前Selector中是否有客户来注册
            int currentNum = selector.select();
            if (currentNum <= 0) {
                // 当前Selector中没有注册的客户
                continue;
            }

            // 获取所有的客户注册信息
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = keys.iterator();
            // 开启轮询机制，进行处理每一个客户注册信息
            while (iterator.hasNext()) {

                // 处理当前注册的客户
                process(iterator.next());

                // 删除当前已经处理的SelectionKey
                iterator.remove();
            }
        }
    }

    /**
     * 处理客户注册信息
     *
     * @param key SelectionKey
     * @throws IOException
     */
    public void process(SelectionKey key) throws IOException {
        if (key.isAcceptable()) {
            SocketChannel client = server.accept();
            client.configureBlocking(false);
            client.register(selector, SelectionKey.OP_READ);
        } else if (key.isReadable()) {
            SocketChannel client = (SocketChannel) key.channel();
            receiveBuffer.clear();
            int len = client.read(receiveBuffer);
            if (len > 0) {
                String msg = new String(receiveBuffer.array(), 0, len);
                receiveMsg(key, msg);
                sessionMsg.put(key, msg);
            }

            client.register(selector, SelectionKey.OP_WRITE);
        } else if (key.isWritable()) {
            if (!sessionMsg.containsKey(key)) {
                return;
            }
            SocketChannel client = (SocketChannel) key.channel();

            sendBuffer.clear();
            sendBuffer.put(new String(sessionMsg.get(key) + ",你好，你的请求已处理").getBytes());
            sendBuffer.flip();

            client.write(sendBuffer);

            client.register(selector, SelectionKey.OP_READ);
        } else if (key.isConnectable()) {

        } else if (key.isValid()) {

        }
    }

    /**
     * 接收消息方法
     */
    public abstract void receiveMsg(SelectionKey key, String message);

    /**
     * 发送消息方法
     */
    public abstract void sendMsg();
}
